/*
 * Copyright (c) 2023 Huawei Technologies Co., Ltd.
 * openUBMC is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *         http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */

#ifndef __STD_SMBUS_H__
#define __STD_SMBUS_H__

#include <mc/core/object.h>
#include <mc/crypto/crc8.h>
#include <mc/engine/base.h>
#include <mc/log.h>

#include "protocol/protocol.h"

constexpr uint8_t DEFAULT_MCU_ADDRESS = 0xD4;

constexpr uint8_t success_code = 0x00;
constexpr uint8_t header_len   = 12;

constexpr uint8_t write_command_code = 0x20;
constexpr uint8_t read_command_code  = 0x21;
// SMBus 错误码映射表
static const std::unordered_map<uint8_t, std::string> error_code_map = {
    {0x01, "opcode not support"},
    {0x02, "parameter error"},
    {0x03, "internal error"},
    {0x0F, "bus busy"}};

// 前向声明，避免跨模块依赖
// namespace dev {
//     class complex_object;
// }

typedef struct std_smbus_request {
    uint8_t  count;
    uint8_t  lun;
    uint8_t  arg;
    uint16_t opcode;
    uint32_t offset;
    uint32_t length;
} std_smbus_request_t;

typedef struct std_smbus_reponse {
    uint8_t     count;
    uint8_t     error_code;
    uint16_t    opcode;
    uint32_t    total_length;
    uint32_t    length;
    std::string data;
} std_smbus_reponse_t;

namespace dev {
// 请求参数模板，定义标准 SMBus 操作所需的参数结构
struct request_params_template {
    bool opcode            = true; // 操作码
    bool offset            = true; // 偏移量
    bool expect_data_len   = true; // 期望数据长度
    bool args              = true; // 参数
    bool data              = true; // 数据
    bool natcj_write       = true; // NATCJ 写操作
    bool data_object_index = true; // 数据对象索引
};

// SMBus 错误码定义
enum class smbus_error : uint8_t {
    SUCCESS              = 0x00, // 成功
    OPCODE_NOT_SUPPORTED = 0x01, // opcode not supported
    PARAMETER_ERROR      = 0x02, // parameter error
    INTERNAL_ERROR       = 0x03, // internal error
    BUS_BUSY             = 0x04  // bus busy
};

// SMBus 状态定义
enum class smbus_status : uint8_t {
    IDLE     = 0x00, // 空闲
    BUSY     = 0x01, // 忙
    COMPLETE = 0x02, // 完成
    ERROR    = 0x03  // 错误
};

class MC_API std_smbus : public mc::core::object {
public:
    std_smbus(mc::engine::abstract_object* parent);
    ~std_smbus();

    virtual void init(mc::mutable_dict& params);
    std::string  send_request(mc::mutable_dict request);
    std::string  write_and_read(mc::mutable_dict& track_request);
    std::string  batch_write(mc::mutable_dict& track_request);
    std::string  write_without_read(mc::mutable_dict& track_request);
    void         append_data(mc::mutable_dict& track_request, const mc::mutable_dict& request);
    std::pair<bool, mc::mutable_dict> write_read(mc::mutable_dict& track_request);
    mc::mutable_dict                  get_response_empty_obj();
    virtual uint32_t                  get_max_frame_len();
    std::string                       pack_smbus_request(const mc::mutable_dict& request);
    std::pair<bool, mc::mutable_dict> unpack_smbus_reponse(const std::string& rsp_bin);
    virtual std::pair<bool, std::vector<uint8_t>> send_and_receive(const std::string& data,
                                                                   uint32_t           len);
    virtual std::pair<bool, std::vector<uint8_t>>
                        send_and_receive_request_in_frames(mc::mutable_dict& track_request);
    void                calc_last_frame_len(mc::mutable_dict& track_request);
    virtual std::string construct_request_data(mc::mutable_dict& track_request);
    virtual std::pair<bool, mc::mutable_dict> unpack_response_data(const std::string& rsp_bin,
                                                                   const int32_t expect_data_len);
    std::pair<bool, mc::mutable_dict>         _unpack_request(const std::vector<uint8_t>& data,
                                                              const mc::mutable_dict&     track_request);

    virtual void register_WriteRead_method(
        std::function<std::vector<uint8_t>(const std::vector<uint8_t>&, uint32_t)> func);

    virtual void register_ComboWriteRead_method(
        std::function<std::vector<uint8_t>(uint32_t, const std::vector<uint8_t>&, uint32_t,
                                           uint32_t)>
            func);

    virtual void register_Write_method(
        std::function<std::vector<uint8_t>(uint32_t, const std::vector<uint8_t>&)> func);
    virtual void register_Read_method(std::function<std::vector<uint8_t>(uint32_t, uint32_t)> func);
    std::pair<bool, std::vector<uint8_t>> send(uint32_t, const std::string&);
    std::pair<bool, std::vector<uint8_t>> read(uint32_t, uint32_t);

    uint8_t m_buffer_len  = 64;
    uint8_t m_mcu_address = 0xD4;

private:
    uint32_t                m_max_retry_count = 3;
    std::string             m_name            = "std_smbus";
    const uint8_t           write_read_delay  = 1; // 10ms
    request_params_template request_params;

    std::string _send_request_internal(mc::mutable_dict& request);
    bool        _concat_response(mc::mutable_dict& final_response, const mc::mutable_dict& rsp_raw);

    mc::mutable_dict _unpack_response(const std::string& rsp_bin);

    std::function<std::vector<uint8_t>(const std::vector<uint8_t>&, uint32_t)> m_WriteRead_func;
    std::function<std::vector<uint8_t>(uint32_t, const std::vector<uint8_t>&)> m_Write_func;
    std::function<std::vector<uint8_t>(uint32_t, uint32_t)>                    m_Read_func;
    std::function<std::vector<uint8_t>(uint32_t, const std::vector<uint8_t>&, uint32_t, uint32_t)>
        m_ComboWriteRead_func;
};

} // namespace dev

#endif /* __STD_SMBUS_H__ */